package pattern.singleton;

/**
 * 懒加载
 */
public class Singleton {

    private static Singleton instance;

    private Singleton() {
    }


    /**
     * 懒汉式，线程不安全
     * 1. 是否 Lazy(懒加载) 初始化：是
     * 2. 是否多线程安全：否
     * 3. 实现难度：易
     * 4. 描述：这种方式是最基本的实现方式，这种实现最大的问题就是不支持多线程。因为没有加锁 synchronized，所以严格意义上它并不算单例模式。
     *
     * @return
     */
    public static Singleton getInstance() {
        if (instance == null) {
            instance = new Singleton();
        }
        return instance;
    }

    /**
     * 懒汉式，线程安全
     * 1. 是否 Lazy(懒加载) 初始化：是
     * 2. 是否多线程安全：是
     * 3. 实现难度：易
     * 4. 描述：这种方式具备很好的 lazy loading，能够在多线程中很好的工作，但是，效率很低，99% 情况下不需要同步。
     * 优点：第一次调用才初始化，避免内存浪费。
     * 缺点：必须加锁 synchronized 才能保证单例，但加锁会影响效率。
     *
     * @return
     */
    public static synchronized Singleton getSyncInstance() {
        if (instance == null) {
            instance = new Singleton();
        }
        return instance;
    }


    /**
     * 饿汉式
     * 1. 是否 Lazy(懒加载) 初始化：否
     * 2. 是否多线程安全：是
     * 3. 实现难度：易
     * 4. 描述：这种方式比较常用，但容易产生垃圾对象。
     * 优点：没有加锁，执行效率会提高。
     * 缺点：类加载时就初始化，浪费内存。基于静态常量，可以避免内存浪费。
     *
     * @return
     */
    private static Singleton beLazySynvInstance = new Singleton();

    public static Singleton getBeLazySyncInstance() {
        return beLazySynvInstance;
    }

    /**
     * 双重检测机制（DCL，即double-checked locking）
     * 1. 是否 Lazy(懒加载) 初始化：是
     * 2. 是否多线程安全：是
     * 3. 实现难度：较复杂
     * 4. 是否多线程安全：是
     * 5. 描述：这种方式采用双锁机制，安全且在多线程情况下能保持高性能。
     */
    private volatile static Singleton beLazyDoubleCheckInstance;

    public static Singleton getBeLazyDoubleCheckInstance() {
        if (beLazyDoubleCheckInstance == null) {
            synchronized (Singleton.class) {
                if (beLazyDoubleCheckInstance == null) {
                    beLazyDoubleCheckInstance = new Singleton();
                }
            }
        }
        return beLazyDoubleCheckInstance;
    }

    /**
     * 登记式/静态内部类
     * 1. 是否 Lazy(懒加载) 初始化：是
     * 2. 是否多线程安全：是
     * 3. 实现难度：一般
     * 4. 描述：这种方式能达到双检锁方式一样的功效，但实现更简单
     * 对静态域使用延迟初始化，应使用这种方式而不是双检锁方式。
     * 这种方式同样利用了 classloder 机制来保证初始化 instance 时只有一个线程，它跟第 3 种方式不同的是：第 3 种方式只要 Singleton 类被装载了，
     * 那么 instance 就会被实例化（没有达到 lazy loading 效果），而这种方式是 Singleton 类被装载了，instance 不一定被初始化
     * <p>
     * 因为SingletonHolder 类没有被主动使用，只有显示通过调用 getInstance 方法时，才会显示装载 SingletonHolder 类，从而实例化 instance
     * <p>
     * 想象一下，如果实例化 instance 很消耗资源，所以想让它延迟加载，另外一方面，又不希望在 Singleton 类加载时就实例化，
     * 因为不能确保 Singleton 类还可能在其他的地方被主动使用从而被加载，那么这个时候实例化 instance 显然是不合适的
     */
    private static class SingletonHolder {
        private static final Singleton INSTANCE = new Singleton();
    }

    public static final Singleton getStaticInstance() {
        return SingletonHolder.INSTANCE;
    }

    /**
     * 枚举
     * 1. JDK版本：JDK1.8 起
     * 2. 是否 Lazy(懒加载) 初始化：是
     * 3. 是否多线程安全：是
     * 4. 实现难度：易
     * 5. 描述：这种方式采用枚举，在单例类中定义一个私有的枚举类，该枚举类的构造函数会自动被调用，并且只会调用一次，
     */
    public static final Singleton getEnumInstance() {
        return SingletonEnum.INSTANCE.getInstance();
    }
}

enum SingletonEnum {
    INSTANCE;

    private final Singleton singleton;

    SingletonEnum() {
        singleton = Singleton.getInstance();
    }

    public Singleton getInstance() {
        return singleton;
    }

    public String whateverMethod() {
        return "do something";
    }
}
